#include "Appender.h"

#include "Meta.h"
#include "Persistence.h"

//#include <iostream>

using namespace leveldb;

Appender::Appender(Stream * stream)
: stream_(stream)
{
    this->open();
}
Appender::~Appender(){
    this->close();
}

leveldb::Status Appender::open() {
    if(isOpen_ == false){
        assert(stream_ != nullptr);

        recover();

        isOpen_ = true;
    }
    return leveldb::Status::OK();
}

//read the records && [truncate] the after offset of last_records
leveldb::Status Appender::recover(){
    if(!stream_->exists()){
        appender_ = stream_->createAppender();
        return leveldb::Status::OK();
    }

    uint64_t offset = 0;
    auto s = stream_->length(offset);
    if(!s.ok()) return s;

    if(offset == 0){
        appender_ = stream_->createAppender();
        return s;
    }

    // stream-length > 0 -> skip(offset)
    s = skipToEnd(sequenceNumber_, offset);
    if(!s.ok()) return s;
    
    appender_ = stream_->createAppender(offset);

    stream_->size().value(sequenceNumber_);
    return s;
}

leveldb::Status Appender::skipToEnd(uint64_t & cntRecord, uint64_t & offset){
    auto * reader = stream_->createReader();
    assert(reader != nullptr);
    for(;reader->hasNext(); reader->next()){
        ++cntRecord;
    }
    //TODO: adjust the offset in file to the offset the last records
    //need to rewrite the reader to get file offset
    return Status::OK();
}

leveldb::Status Appender::close(){
    if(isOpen_ == true){
        sequenceNumber_ = 0;
        delete appender_; appender_ = nullptr;
        stream_ = nullptr;
        isOpen_ = false;
    }
    return leveldb::Status::OK();
}

leveldb::Status Appender::append(const leveldb::Slice & str){
    return this->_append(sequenceNumber_++, str);
}

leveldb::Status Appender::_append(uint64_t key, const leveldb::Slice & value){
    assert(appender_ != nullptr);
    KeyValueBuilder kvp;
    kvp.put(key, value);

    auto s = appender_->append(kvp.contents());
    if(!s.ok()) return s;

    stream_->size().inc();
    return leveldb::Status::OK();
}
